home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 326-350 / disk_348 / ilbmlib / ilbmlib.doc < prev    next >
Text File  |  1992-05-06  |  68KB  |  1,344 lines

  1. Set your editor's TAB width to 3
  2.  
  3.                              THE IFF ILBM LIBRARY MANUAL
  4.                             ­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
  5.                                      by Jeff Glatt
  6.                                  6 Sycamore Drive East
  7.                                  New Hartford, NY 13413
  8.  
  9. *****************************************************************************
  10.  
  11.     1).  Introduction
  12.     2).  Overview
  13.     3).  High Level Routines
  14.     4).  Middle Level
  15.     5).  Handling PROPS
  16.     6).  Custom FORM Handler
  17.     7).  Custom (ILBM) Chunk Handler
  18.     8).  Low Level
  19.     9).  Adapting old applications
  20.     10). ANIM Support
  21.     11). Image Size/Scaling
  22.     12). ILBMLib Error Msgs
  23.     13). Misc Routines
  24.     14). Additional Comments
  25.     15). Acknowledgements
  26.     16). Scary Legal Stuff
  27.  
  28.  
  29. *****************************************************************************
  30. 1). Introduction
  31.  
  32.     The primary purpose of the ilbm.library is to provide functions to read and
  33. write any kind of IFF file.  In particular, there is extra, high level support
  34. for ILBM picture files which simplifies the viewing or saving of a picture.
  35. Furthermore, there are routines useful for creating an ANIM reader/writer/
  36. player.
  37.     The ilbm.library is a disk-based runtime library.  This means that any
  38. application, upon being run, can open and call routines in this library just
  39. like a program might utilize routines in the Intuition or Graphics libraries. 
  40. The ilbm library is easily accessed from C, assembly, and BASIC programs, and
  41. there are examples available in each language.
  42.     The library is based upon the original Electronic Arts IFF code (i.e. Open-
  43. RIFF, GetBODY, etc.) as well as a few Commodore enhancements (i.e. Scheppner's
  44. getBitMap, handleCAMG, etc.).  There have been a few changes to the internal
  45. logic of some routines, but generally, they perform the same functions as the
  46. original code.  A few more significant changes were made to certain routines in
  47. order to accomodate ANIM and non-ILBM files, and also to streamline the parsing
  48. of LISTs, CATs, and PROPs.
  49.     The biggest change between the original code and this library is that the
  50. code has been rewritten in the tightest possible 68000 assembly.  The size of
  51. the library is < 7000 bytes.  A program that uses this library can be smaller
  52. and faster than if the program used the original EA code.¹  Because the library
  53. uses no global data and is therefore re-entrant, many applications can use the
  54. library simultaneously.
  55.     Finally, access to the low level IFF functions is provided.  The routines,
  56. OpenRIFF, GetChunkHdr, IFFReadBytes, IFFWriteBytes, etc. are all included.
  57. These routines are equivilent to the original EA code except that the lib code
  58. is smaller and faster.  Because of this, it is possible to use this library for
  59. any kind of IFF reader and writer with a resulting improvement in the applica-
  60. tion's size and speed.
  61.  
  62.  
  63. *****************************************************************************
  64. 2)  Overview
  65.  
  66.     The library has functions that fall into three levels: high, middle, and
  67. low.  The high level is designed to read or write ILBM files with tremendous-
  68. ly little effort on the part of the application.  The middle level requires
  69. more setup, but allows you to construct a reader/writer for any kind of IFF
  70. file.  This level handles extremely ugly details like LISTs, CATs, error
  71. checking for IFF "weirdness", and DOS I/O while allowing you control over what
  72. is to be done with individual chunks.  The low level maintains DOS I/O and
  73. error checking, but leaves you to deal with LISTs and CATs.  Anything that you
  74. used to do with the original EA code, you should be able to do in an almost
  75. identical manner, and adapting your present IFF code to use the library is
  76. quite easy.
  77.     Most library routines return an IFFP code.  This is a number from 0 to -11,
  78. or in some cases, a 4 byte IFF ID.  0 (IFF_OKAY) usually means success.  The
  79. negative numbers mean errors.  See the INCLUDE files for all possible numbers. 
  80.  
  81.  
  82. *****************************************************************************
  83. 3). High Level Routines
  84.  
  85.     At the highest levels, the type of IFF file that this library is designed
  86. to read and write is an ILBM.
  87.     There are 2 high level functions, LoadIFFToWindow() and SaveWindowToIFF().
  88. The former loads an ILBM into a window, the latter saves a window as an ILBM.
  89. These functions are so comprehensive that they can even be used with inter-
  90. preted languages like BASIC or Rexx.
  91.     For saving a file, SaveWindowToIFF() is passed the filename that you
  92. wish to create, and the pointer to the window (whose screen's bitplanes are)
  93. to be saved in IFF ILBM form.  The library writes out a complete ILBM with
  94. the proper BMHD, CMAP, CAMG, and BODY chunks.  All error checking is done by
  95. the lib.  SaveWindowToIFF() returns an IFFP number.  0 means that the save
  96. was successful.  Any other number is an error.  In the case of error, any
  97. partial file is deleted.
  98.     When reading a file, LoadIFFToWindow() takes the filename that you wish
  99. to load, and a special ILBMFrame structure. See the INCLUDE files for a
  100. description of this structure. The C include file is "ILBM_lib.h". The assembly
  101. include is "IFF.i". Basic users should read "BasicUsers".
  102.     You MUST allocate this structure and initialize certain fields before
  103. using LoadIFFToWindow().
  104.     LoadIFFToWindow() looks for ILBMs inside of CATs and LISTs, sidestepping all
  105. of the other non-ILBM FORMs.  It rummages around inside of such a file looking
  106. for the first ILBM that it can find.  If it finds an ILBM, it does one of two
  107. things:
  108.  
  109.     1). Loads the imagery into a window that you opened, scaling the picture
  110.          to fit the dimensions of the window if that is necessary.  What this
  111.          means is that you can load a HIRES image into an opened LORES window,
  112.          and vice versa, etc.
  113.  
  114.     2). Opens a screen and borderless backdrop window that best "fits" the
  115.          picture (e.g. the proper width, height, depth, and viewmodes.
  116.  
  117.     You determine which method is imployed by how you set up the ILBMFrame
  118. before calling LoadIFFToWindow().  The ILBMFrame has iWindow and iScreen
  119. fields.  If you zero both these fields, then the library will open a screen
  120. and window into which it loads the picture.  The pointers for this new screen
  121. and window will be stored in their respective ILBMFrame fields.  The window
  122. will have a default IDCMP of MOUSEBUTTONS.  If instead, you open your own
  123. window and screen, you should place these addresses into the ILBMFrame fields
  124. before calling LoadIFFToWindow().  The lib will then load the image into that
  125. window.
  126.     Furthermore, you should set up the iUserFlags field.  When certain bits of
  127. this field are set, the library will do such things as make the mouse pointer
  128. invisible, or not change your window's present colormap, etc.  See the INCLUDE
  129. files for details.  Usually, you'll just zero iUserFlags.
  130.     You do not have to initialize any of the other ILBMFrame fields.
  131.     LoadIFFToWindow() returns an IFFP number.  0 means that the load was
  132. successful.  Any other number is an error.
  133.     Note that if the library opens the window/screen for you, the pointers are
  134. returned in the ILBMFrame.  From this point on, these are your responsibility.
  135. You may ModifyIDCMP() the window, or anything else you desire.  You must close
  136. the window and screen when you are finished with them.  Even if LoadIFFToWin-
  137. dow() returns an error, you must still get the iWindow and iScreen fields, and
  138. close any non-zero pointer.  See the example programs for details.
  139.     If neither high level routine serves your purpose, then you will need to
  140. use some mid or low level routines instead.
  141.  
  142. ============================ SaveWindowToIFF ===========================
  143.  IFFP = SaveWindowToIFF(fileName, window)
  144.   d0                       d1       a0
  145.  
  146.  Saves a window's image as an IFF ILBM file. Returns an IFFP code (0 if OK,
  147.  a negative, non-zero number for an error. Z-flag set accordingly.)
  148.  This procedure calls SaveILBM(), passing in an <x, y> location of <0, 0>,
  149.  and a NULL mask. It also assumes you want to write out all the bitplanes
  150.  in the BitMap. Also, it applies byte run compression to the BODY.
  151.  If an error in saving, it deletes the partial file. The fileName should be
  152.  a complete path as might be typed at the CLI (i.e. df0:extras/myName). This
  153.  is an ascii, NULL-terminated string. window is the address of the opened
  154.  window (as returned from Intuition's OpenWindow).
  155.  
  156. ============================= LoadIFFToWindow ===========================
  157. IFFP = LoadIFFToWindow( fileName, ILBMFrame )
  158.  d0                        d1       a1
  159.  
  160.  Loads an ILBM file into the ILBMFrame's iWindow. If iWindow is NULL, opens
  161.  a screen/backdrop window for the image. This routine calls LoadILBM().
  162.  
  163.  
  164. ******************************************************************************
  165. 4). Middle Level
  166.  
  167.     The middle level routines isolate you from dealing with low level struc-
  168. tures, LISTs, and CATs, but give you the option of installing custom routines
  169. to handle FORMs, PROPs, or chunks inside of ILBM FORMs.
  170.     There are two mid level routines for reading/writing ILBMs, LoadILBM()
  171. and SaveILBM().  A third routine, LoadIFF(), is used for non-ILBM forms
  172. such as SMUS.  There is a routine that makes saving ANIMs easy, SaveANIM().
  173. Furthermore, LoadILBM() can easily be setup to parse an ANIM file.
  174.     We will discuss LoadILBM() and SaveILBM() first.
  175.  
  176.     ========================== SaveILBM() ==============================
  177. IFFP=SaveILBM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,handler)
  178.  d0                    d0            d1            d2            d3        a0         a1        a2         a3
  179.  
  180.     ViewModes    - The viewmodes of the screen as an ULONG
  181.     Compress        - Compression type. 0=None 1=cmpByteRun1
  182.     fileHandle    - DOS handle of the opened file
  183.     Mask            - pointer to a mask plane, or NULL if none
  184.     colors        - pointer to the colorTable to save as a CMAP chunk
  185.     BitMap        - pointer to the Bitmap structure whose planes are to be saved
  186.                     as the ILBM BODY
  187.     xyPoint        - pointer to a structure holding two UWORD values that describe
  188.                     the xy position to be saved in the BMHD chunk
  189.     handler        - pointer to a routine to be called before the BODY is written
  190.                     out, or NULL if none
  191.  
  192.     SaveILBM() writes an entire BitMap as a FORM ILBM in an IFF file. Unlike
  193. SaveWindowToIFF, this routine allows you to save an uncompressed image, or with
  194. a different xy position than 0,0 (for saving a portion of the whole image),
  195. or saving additional chunks in addition to BMHD, CAMG, CMAP, and BODY.  It
  196. works for any display mode.
  197.     You must open the IFF file to be written before calling SaveILBM(), and
  198. close the file upon return.
  199.     SaveILBM() writes out a BMHD whose values are determined by the passed
  200. BitMap's size/depth and the xyPoint.  A CAMG chunk is written for the passed
  201. ViewModes.  A CMAP chunk is written for the passed colTable.
  202.     Then, if you have supplied a passed handler, it is called.  This allows an
  203. application to save additional chunks such as CRNG, etc.  The lib passes your
  204. handler a GroupContext structure.  To write a chunk, you must open the Context
  205. (via OpenRGroup()) and use PutCk(), PutCkKnown(), or calls to IFFWriteBytes
  206. (after PutCkHdr, and ending with PutCkEnd).  Your handler should return
  207. IFF_OKAY after it has successfully written out the desired chunks.  Any other
  208. IFFP code will terminate SaveILBM() with that error.
  209.     Finally, SaveILBM() writes the BODY based on your passed compress mode
  210. and your mask plane.
  211.     SaveILBM returns IFF_OKAY if successful. If an error, you must delete the
  212. partial file yourself.
  213.     The utility program IFFCheck would print the following outline of the
  214. resulting file:
  215.  
  216.     FORM ILBM
  217.         BMHD
  218.         CAMG
  219.         CMAP
  220.         ...other chunks that your handler saves
  221.         BODY        (compressed) or (uncompressed)
  222.  
  223.  
  224.     =========================== LoadILBM() ===========================
  225.     IFFP = LoadILBM(fileHandle, vectors, iframe)
  226.     d0                            d1            a0            a1
  227.  
  228.     fileHandle    - DOS handle of an opened file
  229.     vectors        - a pointer to a Vectors structure
  230.     iframe        - a pointer to the master, initialized ILBMFrame structure
  231.  
  232.     Can read an ILBM file. Unlike LoadIFFToWindow, you can arrange for your own
  233.  custom routines to handle FORMs, PROPs, and certain ILBM chunks.  LoadILBM()
  234.  will return IFF_DONE if an image has been successfully loaded into a window by
  235.  the lib's default 'FORM' handler.  All other IFFP codes indicate that an image
  236.  wasn't loaded.
  237.     In the case of custom handlers, normal return is IFF_OKAY when the whole
  238.  file has been scanned.  On the other hand, your custom handler can cause
  239.  LoadILBM() to terminate by returning any IFFP error (i.e. IFF_DONE, END_MARK,
  240.  IFF_BAD, etc).  In this case, LoadILBM() returns that IFFP error.
  241.     ILBMFrame's iWindow, iScreen, and iUserFlags must be initialized.
  242.  
  243.     EXTREMELY modified version of ReadPict.c
  244.     by Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  245.     Modified by C. Scheppner and Jeff Glatt
  246.  
  247.  
  248.     The library has default routines that it calls whenever it encounters a
  249. LIST, CAT, PROP, FORM, or a chunk inside of a PROP or FORM.  LISTs, CATs, and
  250. ILBM PROPs are always handled by the library.  On the other hand, you can
  251. replace the default handlers for non-ILBM PROPs, all FORMs, or all ILBM chunks
  252. with your own custom routines.  For example, the library can call your FORM
  253. handler each time that it encounters a FORM inside of an IFF file.
  254.     Let me explain some details about the default routines.  First, the default
  255. FORM handler looks for ILBMs.  It can be setup to parse ANIMs and other non-
  256. ILBM forms, but it always looks for ILBMs.  For this reason, you should always
  257. supply an ILBMFrame when using the default FORM handler. The handler can find
  258. ILBMs inside of CATs and LISTs, sidestepping all of the other non-ILBM FORMs. 
  259. It rummages around inside of such a file looking for the first ILBM that it can
  260. find.     It handles the following chunks inside an ILBM:
  261.  
  262.     CMAP    BMHD    CRNG    CCRT    CAMG    BODY
  263.  
  264. The CMAP, BMHD, CAMG, CCRT, and CRNG chunks are loaded into the respective
  265. fields of the ILBMFrame (see INCLUDE file).  CCRT are converted to CRNG.  All
  266. other ILBM chunks are ignored.  When it finds the BODY of an ILBM, it does one
  267. of two things:
  268.  
  269.     1). Loads the imagery into a window that you opened, scaling the picture
  270.          to fit the dimensions of the window if that is necessary.
  271.  
  272.     2). Opens a screen and borderless backdrop window that best "fits" the
  273.          picture (e.g. the proper width, height, depth, and viewmodes).
  274.  
  275. This should sound familiar. It is exactly the same as LoadIFFToWindow.  Just
  276. like that high level function, you should set up the ILBMFrame according to
  277. what you want the lib to do.  After the image is loaded, the parsing is stopped
  278. and LoadIFF() returns IFF_DONE if successful.  All other error codes indicate
  279. failure.
  280.     The default PROP handler handles the following chunks inside an ILBM PROP:
  281.  
  282.     CMAP    BMHD    CRNG    CCRT    CAMG
  283.  
  284. These chunks are loaded into the respective fields of the ILBMFrame with CCRT
  285. converted to CRNG. All other ILBM chunks are ignored.  Also, any non-ILBM
  286. PROPs are ignored.
  287.     One of the parameters to LoadILBM() is a Vector structure. This is simply
  288. a structure that holds the pointers to whatever routines you would like the
  289. lib to execute while parsing an IFF file.  These routines replace the lib's
  290. default handlers.  If the Vector pointer is 0, then the default lib routine is
  291. used.  You must initialize this structure before calling LoadILBM()!
  292.     One field holds a pointer to a routine to handle non-ILBM 'PROP's or an
  293. ILBM PROP chunk that is "unknown" to the lib.  The lib doesn't know about the
  294. following ILBM chunks: ANHD, DEST, GRAB, SPRT, and DLTA.  It will skip these
  295. in an ILBM PROP if you don't install a PROPhandler. Also, it will skip nonILBM
  296. PROPs if you don't have a PROPhandler.  If you don't care, set this field to
  297. NULL.
  298.     Another field is for your FORMhandler routine address. If not NULL, this
  299. routine is called instead of the lib's default 'FORM' handler. You are expected
  300. to handle all parsing of FORMs as the library finds them.  This means that you
  301. also have to parse the chunks in the FORM.  If your application does not deal
  302. with ILBMs at all, you will have to install a custom FORM routine (and use
  303. LoadIFF).
  304.     Another field in the Vectors structure is for a CHUNKhandler. This is called
  305. by lib's default 'FORM' handler for all chunks inside an ILBM.  If NULL, the
  306. lib's CHUNKhandler will skip the following ILBM chunks:
  307.  
  308.     ANHD    DEST    GRAB    SPRT    DLTA
  309.  
  310. If you don't use the lib's default 'FORM' handler, then this field is ignored. 
  311. If you supply a CHUNKhandler, you are telling the library, "If you come across
  312. an ID that you don't understand, don't skip it. Instead, give it to me and I'll
  313. take care of that ILBM chunk".
  314.     The last field is for the NonILBMhandler.  This is called by the lib's
  315. default 'FORM' handler when it encounters a FORM other than an ILBM (i.e. 8SVX,
  316. etc).  If NULL, nonILBM FORMs are skipped over in search of ILBMs.  If you
  317. don't use the lib's default 'FORM' handler, then this field is ignored.  If you
  318. supply a NonILBMhandler, you are telling the library, "If you come across a
  319. FORM other than ILBM, don't skip it. Instead, give it to me and I'll take care
  320. of that FORM".  Note that the library still looks for an ILBM to load into a
  321. window when you use the default 'FORM' routine.
  322.     So, by installing routines via the Vectors structure, you can "take away"
  323. certain parsing duties from the lib, while it handles LISTS, CATS, and low
  324. level details.  For example, you could install just a CHUNKhandler and set
  325. the other fields to 0.  The lib would parse all LISTS, PROPS, CATS, and FORMS.
  326. It would only call your routine for each chunk in an ILBM.  See the examples,
  327. ANIMInfo.asm and IFFinfo.c, for details of installing custom routines, and
  328. using mid level routines.
  329.     If your application does not deal with ILBMs, you will need to replace the
  330. default FORMhandler and PROPhandler.  If you are dealing with ANIMs, you will
  331. only need a custom CHUNKhandler to handle DLTA and ANHD chunks, plus a custom
  332. PROPhandler for those chunks.  If you want to load other forms in addition to
  333. ILBMs, then you will need to add a NonILBMhandler.
  334.     Here are the parameters passed to your custom vectors. Return an IFFP code.
  335.  
  336.     IFFP = PROPhandler(chunkID,PropID,Context,Vectors,Frame,PROPList)
  337.     d0                            d0            d2        a0            a2        a3        a4
  338.  
  339.     IFFP = FORMhandler(chunkID,Context,Vectors,Frame,PROPList)
  340.     d0                            d0            a0         a2        a3        a4
  341.  
  342.     NonILBMhandler and CHUNKhandler same args as FORMhandler.
  343.  
  344.     LoadILBM() zeros out the ILBMFrame's iFlags (but not iUserFlags), iBMAP,
  345. iNumColors, and iCycleCnt fields.  You must initialize the iUserFlags field
  346. prior to calling this routine.  Various bits of this field affect certain
  347. options (see Include File for details).  Other bits are set by the lib to
  348. inform you of certain facts. (i.e. the ANIMB bit would be set by the default
  349. 'FORM' handler if an ANIM FORM was encountered in the file.)  You also initial-
  350. ize the iWindow and iScreen fields as per LoadIFFToWindow().
  351.  
  352.  
  353.  
  354.         ====================== LoadIFF() =====================
  355.  
  356.     IFFP = LoadIFF(file, vector, dataAddress)
  357.     d0                    d1            a0            a1
  358.  
  359.     This is just like LoadILBM() except that it is for reading/writing non-ILBM
  360. forms. If you're using this routine, you should definitely set up the Vector's
  361. FORMhandler to point to a custom routine.  Your custom routine should then
  362. parse FORMs using low level functions such as GetFChunkHdr, IFFReadBytes, etc.
  363. The dataAddress parameter is optional. It could be a pointer to a Frame of some
  364. sort (or it could be anything).  The lib will pass this to your FORMhandler
  365. as the Frame parameter. It's up to your FORMhandler to decide what it really is
  366. and what to do with it.
  367.     Normal return is IFF_OKAY if whole file scanned.  On the other hand, your
  368. FORMhandler can cause LoadIFF to terminate by returning any IFFP error (i.e.
  369. IFF_DONE, END_MARK, IFF_BAD, etc).  In this case, LoadIFF() returns that IFFP
  370. error.
  371.  
  372.         ====================== SaveANIM() ======================
  373. IFFP = SaveANIM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,
  374.  d0                        d0            d1            d2            d3        a0        a1            a2
  375.  
  376.        FrameHandler,ANHDaddress)
  377.                 a3                a4
  378.  
  379.  Writes an ANIM file.  Writes the passed BitMap as the first frame (FORM
  380.  ILBM).  Assumes user wants to write out all planes of the BitMap.  Calls
  381.  application FrameHandler() for writing subsequent frames.  Normal return
  382.  result is IFF_OKAY.  Works for any display mode.
  383.  
  384.  The utility program IFFCheck would print the following outline of the
  385.  resulting file:
  386.  
  387.  LIST
  388.     PROP ILBM
  389.         BMHD
  390.         CAMG
  391.         CMAP
  392.         ANHD        ;if passed in
  393.     FORM ANIM
  394.         FORM ILBM
  395.             BODY    (compressed) or (uncompressed)
  396.         ....application routine's saved chunks (i.e. additional FORM ILBMs)
  397.  
  398.     This is similiar to SaveILBM except your FrameHandler is called repeatedly
  399.     (so you can write numerous frames) while return = IFF_OKAY.  Returning
  400.     a 1 stops the Handler loop successfully.  Returning an IFFP error aborts
  401.     the save.  Note that the file written is a LIST with a PROP ILBM.  This is
  402.     so that each frame need not contain identical data.  If the passed ANHD
  403.     address is not NULL, the ANHD chunk will be written in the PROP.  In this
  404.     way, a simple ANIM can be written where the first frame need only contain
  405.     a BODY chunk, and subsequent frames contain a DLTA.  If all successful,
  406.     final return is IFF_OKAY.  If an error, you must delete the partial file.
  407.     If no FrameHandler, pass a 0.
  408.  
  409.  
  410. ******************************************************************************
  411. 5). Handling PROPs
  412.  
  413.     LoadILBM() (or LoadIFF()) creates a special PROPList to take care of any
  414. PROPS in the file.  A PROPList is a linked list structure used to manage Frames
  415. allocated to hold PROP data.   There are 4 routines for allocating, examining,
  416. and freeing frame structures from a PROPList.
  417.     In order to link a Frame structure (i.e. an ILBMFrame) into this list, we
  418. need to "extend" the Frame with a few extra fields at the beginning of the
  419. structure.  We prepend the following fields to the frame, and refer to the
  420. whole thing as a PropFrame.
  421.  
  422.     ULONG        *NextPropFrame;    /* Pointer to the next frame in PROPList */
  423.     LONG        ifID;
  424.     USHORT    PropFrameSize;
  425.  
  426. So an ILBMPropFrame would be as follows:
  427.  
  428. typedef struct {
  429.     ULONG        *NextPropFrame;
  430.     LONG        ifID;
  431.     USHORT    PropFrameSize;
  432.     struct    ILBMFrame PropFrame; /* This is the data part of the PropFrame */
  433.     } ILBMPropFrame;
  434.  
  435.     So an ILBMPropFrame has three extra fields prepended to it. It is 10 bytes
  436. larger than an ILBMFrame.  The actual, imbedded Frame (i.e. after the first
  437. 3 fields) I'll refer to as the data part of the PropFrame.
  438.  
  439. In assembly, the prepended fields are:
  440.  
  441. Next    dc.l [address of next frame in list]
  442. ID        dc.b [the 4 byte type ID]    ;type of PropFrame
  443. Size    dc.w [size]                        ;the size of the entire structure (with the
  444.                                             ;subsequent data part)
  445.  
  446. And an ILBMPropFrame would be as follows:
  447.  
  448. Next    dc.l    0
  449. ID        dc.b    'ILBM'
  450. size    dc.w    SizeOfILBMFrame+10
  451.         ;an ILBMFrame structure immediately follows. This is the data part.
  452.  
  453.  
  454.     If an ILBM PROP (inside of a LIST) is encountered, the library always
  455. allocates an ILBMPropFrame on your behalf, linking it into the PROP list.
  456.     Note that the library ALWAYS takes care of parsing the following chunks in
  457. an ILBM PROP:
  458.  
  459.   BMHD  CMAP  CAMG  CRNG  CCRT
  460.  
  461. It parses the chunk data into the appropriate fields of the ILBMPropFrame. For
  462. other ILBM chunks (i.e. ANHD, DEST, etc), it will call your PROPhandler, pass-
  463. ing the chunkID and a pointer to the newly allocated ILBMPropFrame.  The
  464. PropID will be ID_ILBM.  You are expected to handle loading/parsing the chunk
  465. data, returning IFF_OKAY if successful.  You should either store the chunk's
  466. data somewhere, or use it to modify certain fields of the passed ILBMPropFrame.
  467. Returning any other negative IFFP error terminates LoadILBM() (or LoadIFF)
  468. which also returns that error.
  469.     For non-ILBM PROPs, your custom PROPhandler is also called.  The PropID is
  470. the type ID of the PROP (i.e. SMUS, 8SVX, SAMP).  The chunkID is meaningless.
  471. You should either return IFF_OKAY to ignore the PROP, or parse the PROP using
  472. the low level functions GetPChunkHdr and IFFReadBytes.  A convenient approach
  473. would be to allocate a PropFrame for the IFF type. (It's up to your program to
  474. define other types of PropFrames. What's an SMUSPropFrame look like? I don't
  475. know.)  Then link it into the PROPList.  Use GetPROPStruct() to do all this. 
  476. Then parse the PROP chunk data into this allocated PropFrame.  Later, your
  477. custom FORMhandler or NonILBMhandler can retreive the desired PropFrame via
  478. SearchPROP().  Your PROPhandler should return an IFFP code. Anything but
  479. IFF_OKAY aborts the load with that error.
  480.  
  481.     If no custom PROPhandler, non-ILBM PROPs and ILBM chunks ANHD, DEST, GRAB,
  482. SPRT, and DLTA are ignored.
  483.  
  484.  
  485.     ======================= GetPROPStruct ======================
  486.  Frame = GetPROPStruct(size,typeID,PROPList)
  487.    d0                   d0    d1      a1
  488.  
  489.     Allocates a PropFrame of passed size, and stores its ID, links it at the
  490. tail of the passed PROPList, and increments PROPList's entries. Returns a
  491. pointer to the PropFrame's data part (imbedded Frame), or 0 if error.
  492.     Note that a pointer to the data part is returned (i.e. skips those first
  493. 3 "extra" fields) so that you can treat the returned pointer just like a
  494. regular frame.  If you were allocating an ILBMPropFrame, the returned pointer
  495. would be that of an ILBMFrame.
  496.     The size must include the 3 extra fields.
  497.  
  498.  
  499.     ======================== SearchPROP() =====================
  500.  Frame = SearchPROP(ID,PROPList)
  501.   d0                d0    a1
  502.  
  503.     Searches the passed PROPList for the last PropFrame with the same ID as
  504. passed. If it finds one of the same type, returns the address of the data part
  505. (the imbedded frame), or 0 if none of that type found in the list.
  506.     Used by your custom FORMhandler to search the PROPList for any PropFrames
  507. with the same ID.
  508.  
  509.  
  510.     ======================== CopyILBMProp() ======================
  511.  CopyILBMProp(FromFrame,ToFrame)
  512.                   d0        a1
  513.  
  514.     Copies FromFrame to ToFrame. Both frames must be ILBM frames. It transfers
  515. the iUserFlags field of FromFrame to ToFrame without altering any set bits in
  516. ToFrame's iUserFlags.
  517.     If SearchPROP() returns an Frame, you'll want to copy that Frame to the
  518. Frame you intend to use to parse the FORM.  This is a routine for copying
  519. ILBMFrames. You'll need to write routines to copy other Frames that you devise.
  520.  
  521.  
  522.     ======================= FreePROPList() ======================
  523.  FreePROPList(PROPList)
  524.                 a1
  525.  
  526.     Frees all the PropFrames in the passed PROPList.  Normally, LoadILBM() and
  527. LoadIFF() do this.  When using LoadILBM() or LoadIFF(), the lib allocates and
  528. initializes the PROPList. Those routines also free up that PROPList and all its
  529. PropFrames upon termination of the load.  This function is provided in case you
  530. are maintaining a second PROPList that you create and need to free.
  531.  
  532.  
  533. *****************************************************************************
  534. 6). Custom FORM handler
  535.  
  536.     You'll need a custom FORM handler only if you aren't interested in ILBMs.
  537. In this case, you'll also use LoadIFF().
  538.     When the lib encounters a FORM, your custom FORM handler is passed a pointer
  539. to an initialized Context structure (to be discussed later).  Although no
  540. further initialization of this structure is required on your part, you may need
  541. it for calling certain low level lib functions. You are also passed your master
  542. ILBMFrame so that you can save data to it from within your FORMhandler. You are
  543. passed the type ID of the FORM (i.e. is it an 'ILBM'?).  Also, you are passed
  544. a pointer to the PROP list.  One of the first things you'll probably want to do
  545. is search it for any PropFrame with the same type ID as the FORM.  The function
  546. SearchPROP does this.  You may then copy that Frame's data into the Frame that
  547. is used to hold data within your FORMhandler.  CopyILBMProp() can copy an
  548. ILBMPropFrame to your master ILBMFrame.  Then, as you parse chunks in your
  549. FORMhandler, the new data will overwrite the corresponding PROP data (as it
  550. should).  You also are passed the Vectors structure address in case you need it
  551. further.  Your FORMhandler is expected to parse the FORM (using a few low level
  552. routines) and eventually return an IFFP code.  Anything but IFF_OKAY aborts the
  553. load.
  554.     You should use GetFChunkHdr() to read the header of each chunk inside a
  555. FORM.  You pass this the GroupContext structure (which the lib passed to your
  556. custom FORM handler).  It returns an IFFP code.  This should be the chunk ID
  557. if all went well.  Otherwise, it will be a negative IFFP number. (Remember,
  558. legal IFF IDs are positive numbers).
  559.  
  560.     ID = GetFChunkHdr(context)
  561.     d0                            a0
  562.  
  563.     The exception to this is ANIM files.  You should use GetF1ChunkHdr for
  564. ANIM files since ANIMs contain imbedded FORMs (ILBMs).  Note that this will
  565. cause the lib to reenter your custom FORM routine for each "frame" of the
  566. ANIM.  See IFFinfo.c for an example of a custom FORM routine handling ANIMs
  567. and ILBMs.  The return is the same as GetF1ChunkHdr.
  568.  
  569.     ID = GetF1ChunkHdr(context)
  570.     d0                            a0
  571.  
  572.  
  573.     ID = GetPChunkHdr(context)
  574.     d0                            a0
  575.  
  576.  
  577.  
  578. ******************************************************************************
  579. 7). Custom (ILBM) CHUNK Handler
  580.  
  581.  
  582.     You'll use a custom CHUNKhandler only when you also use the default FORM-
  583. handler.  By using a custom CHUNK handler, you determine what is done with
  584. each chunk in an ILBM.  You are passed the chunkID so that you can determine
  585. what you are dealing with (i.e. a CMAP, a DLTA, etc.).  If you don't want the
  586. chunk, simply return IFF_OKAY.  Otherwise, you are responsible for parsing
  587. the rest of the chunk.  The size is gotten via the passed GroupContext and the
  588. ChunkMoreBytes macro (see INCLUDE files).  NOTE: Do not use DOS Write() to
  589. read in bytes.  Always use IFFReadBytes().
  590.     If you would prefer the library to handle the chunk, return the chunkID.
  591. The library can handle CMAP, CRNG, CCRT, BMHD, CAMG, and BODY chunks.  For the
  592. first five chunks, the data is parsed into the ILBMFrame.  If a BMHD or a CAMG,
  593. the respective iFlags bit is set so that you'll know that this property was
  594. found.  For CMAP, the iNumColors reflects how many color regs were loaded into
  595. iColorTable.  For the BODY chunk, the lib decompresses the image into the
  596. ILBMFrames iWindow (or opens a window if NULL), sets the screen's colors to the
  597. iColorTable values, and terminates LoadILBM() with IFF_DONE.
  598.     You might not want the lib to decompress the BODY.  Perhaps you are writing
  599. an ANIM reader where you don't want the lib to actually load the first frame
  600. into some window's bitmap's planes.  You may prefer to decompress BODY and DLTA
  601. chunks during playback.  You can prevent the lib from doing this by handling
  602. ID_BODY yourself (in your CHUNKhandler) rather than returning this ID.
  603.     If you handle a chunk yourself, your CHUNKhandler should return IFF_OKAY
  604. to continue parsing the file, or any other negative IFFP number (including
  605. IFF_DONE) to stop the load.  It is recommended that you use IFF_DONE to
  606. successfully abort a load since this is what the default FORM handler
  607. does when it reaches an ILBM BODY.  This will insure that LoadILBM() always
  608. returns IFF_DONE for aborting the load successfully, END_MARK if you parsed the
  609. entire file without aborting, and other IFFP codes for error conditions.
  610.  
  611. NOTE: The ANIMFLAG bit of iUserFlags will be set if you are parsing an ANIM
  612.         FORM.  Remember that the lib calls your CHUNKhandler for ANHD and DLTA,
  613.         so for each DLTA you encounter, that is the end of another ANIM Frame
  614.         (imbedded ILBM FORM).  If ANIMFLAG is not set, then you are in a plain
  615.         ILBM file.
  616.  
  617.  
  618. ******************************************************************************
  619. 8). Low Level
  620.  
  621.     If the high level functions LoadIFFToWindow() and SaveWindowToIFF(), or
  622. the mid-level functions SaveILBM(), LoadILBM(), and LoadIFF() aren't suitable,
  623. you may use the low level routines to create a reader/writer.
  624.     You will have to keep track of LISTs, CATs, and PROPs as there are no
  625. Vectors Structure, PropFrames, and custom handlers at this level.
  626.     The low level routines are the same routines as the original EA code, IFFr.c
  627. and IFFw.c, except that they are smaller and faster asm modules.
  628.     At this point, I need to warn you that the arguments passed to some routines
  629. have been changed, as well as the order of the arguments. This was done to make
  630. the C interface code smaller and faster in assembly by using the movem in-
  631. struction. Mostly, the changes are trivial. In fact, the documentation for
  632. these functions is almost the original text.
  633.     Unless otherwise stated, the normal (successful) return codes for the low
  634. level routines is IFF_OKAY.  The Z and N Flags are set appropriately for low
  635. level functions. This means that assembly programmers can always bne to an
  636. error routine.
  637.     These routines ASSUME that they're the only ones reading/writing to the
  638. file.  You should only use these routines to read, write, or skip chunks in
  639. an IFF file.  Do not use DOS Read, Write, or Seek.
  640.     The library's low level routines utilize a 36 byte structure called a
  641. "GroupContext". The GroupContext structure that the library uses is identical
  642. to the original EA structure. Check the INCLUDE files for a description.
  643. The Frame field is now called UserData, but you can use it to pass a pointer
  644. to a Frame from level to sublevel.
  645.     For the high and mid-level routines, the lib allocates a new Context (and
  646. initializes it by OpenRIFF or OpenRGroup) for every group (FORM, CAT, LIST,
  647. or PROP) encountered. This is done for you even if you have custom vectors
  648. for FORMs and PROPs.  For low level, you need to do this yourself.  Any
  649. source code written with the original EA code may be used as an example.
  650.     You will only need to deal with Context structures if you are using the
  651. low level routines. This structure is for reading and writing groups and their
  652. chunks. It's just a linked node type of structure for reading (nested) chunks.
  653. A Context structure must be created whenever another level is encountered.
  654. The parentContext field contains the address of another Context structure if
  655. this is not the top level. For example, if a LIST is encountered, a Context
  656. structure is created. For the first FORM in the LIST, another context structure
  657. is created. The parentContext field of the FORM's Context would contain the
  658. address of the LIST's Context structure. The parentContext field of the LIST's
  659. Context structure would be NULL (as long as it wasn't inside of still amother
  660. group). You must read or skip over all the chunks in the FORM before you try to
  661. examine what comes next in the LIST. You can read a chunk via GetChunkHdr() and
  662. IFFReadBytes. You can skip a chunk by calling GetChunkHdr() a second time for
  663. the next chunk's ID.
  664.     The UserData field is set to the parent's Userdata field by certain
  665. routines. In this way, every Context's UserData is the same as its parentCon-
  666. text's UserData.
  667.     If you use the low level routines properly, the lib will take care of
  668. padding out chunks to even bytes, and keeping track of the total bytes read
  669. in or written out.
  670.     The bound field of the Context merits special mention in that it appears to
  671. have no useful purpose. If you place any value other than UNKNOWN there, it
  672. serves as the MAX number of bytes that can be written. So for example, if the
  673. bound = 10000, then you will be prevented from writing out a total of more than
  674. 10000 bytes. For most IFF applications, it isn't initially known how many bytes
  675. will eventually be written. All of the IFF writers which I've seen always set
  676. this to UNKNOWN.  Also, it doesn't seem useful to have the code impose a
  677. barrier.  AmigaDOS already returns an error if you attempt to write beyond a
  678. disc's capacity.  I can only assume that this "feature" was included so that
  679. this code would work on other computers with stranger DOS than the Amiga. This
  680. field is used in PutCkEnd to determine whether the lib needs to adjust UNKNOWN
  681. chunkSizes to the real value after it writes out a chunk. If EA had used a bit
  682. flag for UNKNOWN rather than a value to be compared against the bound field,
  683. then the code could be smaller and faster. Then again, the original code was
  684. written in C, a language invented by and for people who can't be bothered with
  685. trivial details like setting bits.
  686.  
  687.                     ========= Low Level Reader Routines ========
  688.  
  689.     For reading a chunk, the procedure is to allocate a Context structure,
  690.  initialize it with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
  691.  (and its kin) and IFFReadBytes, and close the Context with CloseRGroup or
  692.  EndRGroup.
  693.  
  694.   IFFP = OpenRIFF(fileHandle, Context)
  695.    d0                 d1        a0
  696.  Given an open file, this initializes a Context spanning the whole file.
  697.  ASSUMES context was allocated by caller but not initialized.
  698.  ASSUMES caller doesn't deallocate the context before calling CloseRGroup.
  699.  Returns NOT_IFF IFFP code if the file is too small for even a chunk header.
  700.  
  701.  
  702.  FileSize = FileLength(fileHandle)
  703.     d0                    d1
  704.  Returns the length of the whole file or else a negative IFFP error code of
  705.  NO_FILE (fileHandle=0) or DOS_ERROR.  Does not change your current position
  706.  in the file.
  707.  
  708.  
  709.  IFFP = OpenRGroup(parent, new)   passed 2 Context structures
  710.   d0                 a0     a1
  711.  Open the remainder of the current chunk as a group read context.
  712.  This will be called just after the group's subtype ID has been read
  713.  (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
  714.  remainder is a sequence of chunks.
  715.  This sets new's UserData = parent's UserData.
  716.  ASSUMES new context allocated by caller but not initialized.
  717.  ASSUMES caller doesn't deallocate the context or access the parent context
  718.  before calling CloseRGroup on the new context.
  719.  BAD_IFF error if context end is odd or extends past parent.
  720.  
  721.  
  722.  IFFP = CloseRGroup(context)
  723.   d0                  a0
  724.  Close a group read context, updating its parent context.
  725.  After calling this, the old context may be deallocated and the parent
  726.  context can be accessed again. It's okay to call this particular procedure
  727.  after an error has occurred reading the group.
  728.  
  729.  
  730.  ID = GetChunkHdr(context)
  731.  d0                  a0
  732.  Skip any remaining bytes of the previous chunk and any padding, then
  733.  read the next chunk header into context's chunkID and chunkSize fields.
  734.  If the chunkID is LIST, FORM, CAT, or PROP, this automatically reads the
  735.  subtype ID into context's subID field.
  736.  Caller should dispatch on groupID (and typeID) to an appropriate handler.
  737.  RETURNS the chunkID (the 4 ascii bytes of the new chunk header) if it found
  738.  another chunk. Otherwise, it will return one of the following errors:
  739.   1). END_MARK if there are no more chunks in this context
  740.   2). NOT_IFF if at the top level and it isn't a FORM, LIST, or CAT
  741.   3). BAD_IFF if a malformed chunk, the chunkSize is negative or too big for
  742.       containing context, ID isn't positive, or we hit end-of-file.
  743.  Note that if an error, bit #31 of d0 will be set and so you can either
  744.   1).  move.l d0,d1
  745.        bmi    to an error routine.
  746.   2).  btst.l #31,d0
  747.        bne    to an error routine.
  748.  See also GetFChunkHdr and GetPChunkHdr, below.
  749.  
  750.  
  751.  ID = GetFChunkHdr(context)
  752.  d0                        a0
  753.  Used by a FORM handler. It simply calls GetChunkHdr and returns BAD_IFF if
  754.  it encounters an imbedded PROP.
  755.  
  756.  
  757.  ID = GetPChunkHdr(context)
  758.  d0                        a0
  759.  Used by a PROP handler. It simply calls GetChunkHdr and returns BAD_IFF if
  760.  it encounters an imbedded PROP, LIST, CAT, or FORM.
  761.  
  762.  
  763.  IFFP = IFFReadBytes(nBytes, context, buffer)
  764.     d0                            d0            a0        a1
  765.  Read the specified number of data bytes of current chunk. (Use OpenGroup,
  766.  etc. instead to read the contents of a group chunk.) You can call this
  767.  several times to read the data piecemeal.
  768.  CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > remaining bytes
  769.  which could be due to a application bug or a chunk that's shorter than it
  770.  ought to be (bad form). (If CLIENT_ERROR or SHORT_CHUNK, IFFReadBytes won't
  771.  read any bytes.)
  772.  
  773.  
  774.  IFFP = SkipFwd(bytes, context)
  775.     d0                    d1            a0
  776.  Skip over bytes in a chunk. Won't go backwards.
  777.  Updates context's position but not context's bytesSoFar.
  778.  
  779.  
  780.  NOTE: The original EA code's SkipGroup() routine has been eliminated
  781.  since the library is set up to automatically parse LISTS and PROPS.
  782.  
  783.  
  784.  IFFP = GetCMAP(Context, colorMap, pNColorRegs)
  785.     d0                    d0             a0            a1
  786.  Reads in a CMAP chunk and creates the colorMap at passed address.
  787.  pNColorRegs is passed in as a pointer to the number of ColorRegisters
  788.  caller has space to hold.  GetCMAP sets to the number actually read.
  789.  
  790.  
  791.  IFFP = GetBODY( bitmap, mask, context, BMHD )
  792.     d0                    d0            d1        a0            a1
  793.  Reads the BODY into the passed bitmap's planes, decompressing if necessary.
  794.  Passed the addresses of a BitMap, mask plane (or NULL), the context
  795.  structure, and the loaded BitMap header chunk.
  796.  
  797.  
  798.  BOOL = UnPackRow(dstBytes0, srcBytes0, Source, Dest)
  799.     d0                        d1                d3            a2         a3
  800.    Converts data from "cmpByteRun1" run compression.
  801.     control bytes:
  802.      [0..127]   : followed by n+1 bytes of data.
  803.      [-1..-127] : followed by byte to be repeated (-n)+1 times.
  804.      -128       : NOOP.
  805. Unpacks one row, returning the source and destination addresses when it
  806. produces dstBytes bytes. This routine no longer uses POINTERS to POINTERS
  807. as in the original Elec Arts code. Also, args in a different order.
  808.  
  809.             =========== Low Level Writing (Save) Routines ==========
  810.  
  811.  These routines will random access back to set a chunk size value when the
  812.  caller doesn't know it ahead of time (UNKNOWN size).
  813.  
  814.  The overall scheme is to open an output Group Context via OpenWIFF or
  815.  OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
  816.  each chunk, then use CloseWGroup to close the Group Context.
  817.  
  818.  To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
  819.  its chunks, then call EndWGroup. StartWGroup automatically writes the
  820.  group header and opens a nested context for writing the contents.
  821.  EndWGroup closes the nested context and completes the group chunk.
  822.  
  823.  IFFP = OpenWIFF(limit, fileHandle, new)   new is a pointer to a GroupContext
  824.     d0                    d0            d1            a0     structure
  825.  Given a file open for output, initialize a new, write context.
  826.  The "limit" arg imposes a fence or upper limit on the logical file
  827.  position for writing data in this context. Pass in UNKNOWN to be limited
  828.  only by disk capacity.
  829.  ASSUMES new context structure allocated by caller but not initialized.
  830.  ASSUMES caller doesn't deallocate the context before calling CloseWGroup.
  831.  The caller is only allowed to write out one FORM, LIST, or CAT in this top
  832.  level context (see StartWGroup and PutCkHdr).
  833.  CLIENT_ERROR if limit is odd.
  834.  
  835.  
  836.  IFFP = StartWGroup(groupType, groupSize, subtype, parent, new)
  837.   d0                   d0         d1        d2        a0    a1
  838.  parent and new are the addresses of Context structures, groupType and
  839.  subtype are IDs, groupsize is a LONG
  840.  Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
  841.  nested context. The groupSize includes all nested chunks + the subtype ID.
  842.  The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
  843.  in FILLER ("    ") if it's a mixture of different kinds.
  844.  This writes the chunk header via PutCkHdr, writes the subtype ID via
  845.  IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
  846.  chunks and finish by calling EndWGroup.
  847.  The OpenWGroup call sets new's ILBMFrame to parent's ILBMFrame.
  848.  ASSUME new context structure allocated by caller but not initialized.
  849.  ASSUME caller doesn't deallocate the context or access the parent context
  850.  before calling CloseWGroup.
  851.  ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup.
  852.  
  853.  
  854.  IFFP = OpenWGroup(parent, new)  parent and new are GroupContext structures
  855.   d0                 a0    a1
  856.  Open the remainder of the current chunk as a group write context.
  857.  This is normally only called by StartWGroup.
  858.  Any fixed limit to this group chunk or a containing context will impose
  859.  a limit on the new context.
  860.  This will be called just after the group's subtype ID has been written
  861.  so the remaining contents will be a sequence of chunks.
  862.  This sets new's UserData = parent's UserData.
  863.  ASSUME new context structure allocated by caller but not initialized.
  864.  ASSUME caller doesn't deallocate the context or access the parent context
  865.  before calling CloseWGroup.
  866.  CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first.
  867.  
  868.  
  869.  IFFP = EndWGroup(old)
  870.   d0               a0
  871.  End a group started by StartWGroup.
  872.  This just calls CloseWGroup and PutCkEnd.
  873.  ERROR conditions: See CloseWGroup and PutCkEnd.
  874.  
  875.  
  876.  IFFP = CloseWGroup(old)  old is the address of a Context structure
  877.   d0                a0
  878.  Close a write context and update its parent context.
  879.  This is normally only called by EndWGroup.
  880.  If this is a top level context (created by OpenWIFF) we'll set the file's
  881.  EOF (end of file) but won't close the file.
  882.  After calling this, the old context may be deallocated and its parent
  883.  context can be accessed again.
  884.  Amiga DOS Note: There's no call to set the EOF. We just position to the
  885.  desired end and return. Caller must Close file at that position.
  886.  CLIENT_ERROR if PutCkEnd wasn't called first.
  887.  
  888.  
  889.  IFFP = PutCk(chunkID, chunkSize, context, data)
  890.   d0             d0       d1        a0      a1
  891.  Writes a whole chunk to a Context. This writes the chunk ID, chunkSize,
  892.  data bytes, and (if needed) a pad byte. It also updates the Context to
  893.  reflect how many bytes have been written to the file. Returns CLIENT_ERROR
  894.  if chunkSize = UNKNOWN. This is because you must know how many bytes of
  895.  data you wish to write in order to use this routine. (i.e. Use this routine
  896.  instead of a PutCkHdr/IFFWriteBytes/PutCkEnd series of calls when you know
  897.  exactly how many bytes will be in the chunk). See also PutCkHdr errors.
  898.  
  899.  
  900.  IFFP = PutCkHdr(chunkID, chunkSize, context)
  901.   d0                d0        d1        a0
  902.  Writes just an 8 byte chunk header. The chunk header consists of the 4 byte
  903.  ascii ID, and the chunkSize LONG. You should follow this will any number of
  904.  calls to IFFWriteBytes in order to write out the chunk data. Finally, when
  905.  all the chunk data is output, call PutCkEnd.
  906.  If you don't yet know how big the chunk is, pass in chunkSize = UNKNOWN,
  907.  then PutCkEnd will set the chunkSize for you later. This method is used
  908.  when you really don't know how many data bytes will eventually get written
  909.  out. (i.e. maybe you're compressing the data as it's being written out and
  910.  you don't want to bother with knowing or keeping track of how many bytes
  911.  have been written out. The lib does this for you as long as you specify
  912.  chunkSize = UNKNOWN and you use only the IFFWriteBytes routine to write data
  913.  to the file).
  914.  Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
  915.  number of bytes get written.
  916.  CLIENT_ERROR if the chunk would overflow the Context's bound, if
  917.  PutCkHdr was previously called without a matching PutCkEnd, if chunkSize
  918.  < 0 (except UNKNOWN), if you're trying to write something other
  919.  than one FORM, LIST, or CAT in a top level (file level) context, or
  920.  if chunkID <= 0 (these illegal ID values are used for error codes).
  921.  
  922.  
  923.  IFFP = IFFWriteBytes(nBytes, context, data)
  924.   d0                    d0       a0     a1
  925.  Write nBytes number of data bytes for the current chunk and update the
  926.  Context.
  927.  Returns CLIENT_ERROR if any of the following conditions:
  928.    1). Writing nBytes of data would overflow the Context's limit or
  929.        current chunk's chunkSize. If you have specified these fields to be
  930.        UNKNOWN, then this condition is not applicable.
  931.    2). If PutCkHdr wasn't called first.
  932.    3). nBytes < 0 (i.e. ridiculously large).
  933.  
  934.  
  935.  IFFP = PutCkEnd(context)
  936.   d0               a0
  937.  Complete the current chunk, write a pad byte if needed, and update the
  938.  Context.
  939.  If current chunk's chunkSize = UNKNOWN, this goes back and sets the
  940.  chunkSize in the file.
  941.  CLIENT_ERROR if PutCkHdr wasn't called first, or if the application hasn't
  942.  written 'chunkSize' number of bytes with IFFWriteBytes.
  943.  
  944.  
  945.  IFFP = InitBMHdr(masking, compression, transparentColor,
  946.   d0               d0          d1              d2
  947.                 pageWidth, pageHeight, bmHdr0, bitmap)
  948.                     d3         d4        a0      a1
  949.  Initializes a BitMap Header (BMHD) chunk to the passed values, and sets the
  950.  BMHD's aspect ratio.
  951.  
  952.  
  953.  IFFP PutCMAP(depth, context, colorMap)
  954.   d0            d0     a0        a1
  955.  Writes out the passed colorMap (actually colorTable) as a CMAP chunk.
  956.  
  957.  
  958.  IFFP = PutBODY(mask, context, bmHdr, bitmap)
  959.   d0             d0     d1       a0     a1
  960.  Writes the BODY chunk to disk in compressed or uncompressed form.
  961.  
  962.  
  963.  bytes, newSource, newDest = PackRow(rowSize, pSource, pDest)
  964.   d0       a0         a1                d0       a0      a1
  965.  Given addresses of source and dest, packs one row, returning the new source
  966.  and destination addresses in a0 and a1. RETURNs count of packed bytes in d0.
  967.  Please note that this routine needs the actual addresses to the source and
  968.  destination buffers unlike the original EA code which wanted PTRS to PTRS.
  969.  That technique is unnecessary for assembly applications which can access
  970.  multiple return values in several registers. I decided to put the ineffic-
  971.  iency where it really belongs; in the C application. For C programmers, the
  972.  new source and dest addresses can be found at sourceptr and destptr respec-
  973.  tively. These are globals contained in the module ILBMInterface.asm which
  974.  must be assembled and linked with your application. These globals can be
  975.  accessed after a call to PackRow so that you'll have the addresses to pass
  976.  on the next, subsequent call. Of course, you'll be able to access the
  977.  returned number of packed bytes as a normal return. Ultimately what this
  978.  means is that if you call PackRow from a C application, you can never make
  979.  that application fully re-entrant (unless you put a FORBID/PERMIT around
  980.  the call).
  981.  
  982.  
  983. **************************************************************************
  984. 9). Adapting old IFF applications
  985.  
  986.     If you already have an old application that uses the Electronic Arts code,
  987. there are several approaches that you could take to adapt the program to use
  988. the ilbm lib instead.
  989.     The easiest approach is to ask yourself, "Can I use LoadIFFToWindow() or
  990. SaveWindowToIFF()?"  If all that you're doing with IFF is saving or loading
  991. ILBM pictures in standard Intuition windows, the answer is yes.  Get rid of ALL
  992. the EA code and INCLUDE files. Dump ReadPict.c, ReadIFF.c, IFFr.c, etc.  Follow
  993. the example ShowPic.c (or ShowPic.asm).  Then check the size of your program.
  994.     If you're doing ANIMs, non-ILBM (SMUS, 8SVX, etc.) or overscan screen views,
  995. you'll need to persue a second approach.  Use all of the low level routines of
  996. the lib in place of the similiarly named EA functions.  Section 5 of this
  997. document lists all of the low level functions (i.e. GetCMAP, OpenWGroup, PutCk,
  998. GetBODY, etc.)  Remember that these are functionally identical to the original
  999. EA code.
  1000.     For example, you've probably got OpenRGroup() somewhere in your program.
  1001. (Check the EA code that you're using as well).  Found OpenRGroup()?  Did you
  1002. modify it?  No? Then you're all set.  Rip the function OpenRGroup() out of your
  1003. code. Add the line
  1004.  
  1005.     #include "ilbm_lib.h"
  1006.  
  1007. at the top of the source code. You are now using the ilbm lib's OpenRGroup()
  1008. instead of the original EA code.
  1009.     Do this with all of the low level routines you find throughout your program.
  1010.     Just be careful to note the order of the parameters. They may have changed,
  1011. and if so, you will have to modify any routine that calls the replaced func-
  1012. tion.  For example, a call to the EA routine, PutCkHdr() is as follows:
  1013.  
  1014.     PutCkHdr(context, ckID, ckSize);
  1015.  
  1016. The ilbm lib's PutCkHdr has a different order for those 3 args:
  1017.  
  1018.     PutCkHdr(ckID, ckSize, context);
  1019.  
  1020. Otherwise, the two functions are the same.  Several low level routines have a
  1021. different arg order.  This was done for the sake of efficiency in the lib's
  1022. C interface.  Also, note that a few low level functions no longer need
  1023. certain args.  For example, the EA GetBODY() is:
  1024.  
  1025.    GetBODY( context, bitmap, mask, bmHdr, buffer, bufsize );
  1026.  
  1027. The ilbm lib is:
  1028.  
  1029.     GetBODY( bitmap, mask, context, bmHdr );
  1030.  
  1031. The library supplies its own buffer. You don't have to. (That's one more thing
  1032. you can get rid of in your source code.)
  1033.     Finally, you should get rid of any EA INCLUDE files, and all INCLUDE
  1034. statements in your program that reference those files.  You don't need them.
  1035. "ilbm_lib.h" replaces all EA include files (i.e. IFF.h, PutPict.h, ReadPict.h,
  1036. Packer.h, and ILBM.h).  When you link your program, you will have to assemble
  1037. the file "ILBMInterface.asm", and link with that.
  1038.     Of course, if you can make use of the mid level functions by rewriting your
  1039. program to use all of their special features, your program will be that much
  1040. smaller and faster. Unfortunately, this is not as straightforward as substitu-
  1041. ting the low level functions.
  1042.  
  1043.  
  1044. *****************************************************************************
  1045. 10). ANIM Support
  1046.  
  1047.     There are a few routines to assist in the construction of an ANIM player.
  1048. These routines can unpack BODY and DLTA chunks, modifying a BitMap's planes
  1049. in order to construct the next frame of an animation. Also there is a routine
  1050. to setup a BitMap structure initially based on a loaded BMHD chunk.
  1051.     The normal procedure for an anim player would be to either replace the lib's
  1052. default 'FORM' handler with a custom handler, or use a CHUNKhandler set up to
  1053. handle ID_BODY, ID_ANHD, and ID_DLTA.  This handler should load the first
  1054. frame's BMHD and BODY, and the ANHD and DLTA for all subsequent frames. Also,
  1055. you should set up a PROPhandler to parse any ID_ANHD in an ILBM PROP.
  1056.     Then you can play back the anim as follows:
  1057.  
  1058.  1).    Allocate a raster large enough to hold the decompressed image. You can
  1059.         determine the size by the fields in the BMHD.
  1060.  
  1061.         height = BMHD's h or pageHeight, whichever is larger
  1062.         planedepth = numPlanes * height
  1063.         width = BMHD's w or pageWidth, whichever is larger
  1064.         RowBytes = (15+width)/8, rounded down to the nearest integer
  1065.         RasterSize = RowBytes * planeDepth
  1066.         get CHIP mem for the raster
  1067.  
  1068.  2).    Setup the BitMap structure to be used for displaying the ANIM by calling
  1069.         the ilbm lib's SetupBitMap. Pass your allocated raster to SetupBitMap.
  1070.         You will need to set up the View, ViewPort, and colorMap yourself.
  1071.  
  1072.  3).    Decompress the BODY into the BitMap's planes (i.e. your allocated
  1073.         raster) using the library's DecompBODY(). For double-buffered animation,
  1074.         you will need to setup an identical Bitmap/raster and copy the first
  1075.         image into the new raster.
  1076.  
  1077.  4).    Display the BitMap's planes (i.e. the first frame).
  1078.  
  1079.  5).    Modify the BitMap's planes for the next frame via DecompDLTA().
  1080.  
  1081.  6).  Repeat from step 4 until no more frames.
  1082.  
  1083.  
  1084.     Here are the 3 routines that you'll use in an ANIM player.
  1085.  
  1086.     ===================== SetupBitmap() ===================
  1087.  SetupBitmap(raster,bitmap,BMHD)
  1088.                d0     a0    a1
  1089.  
  1090.  Initializes passed bitmap's depth, rows, and BytesPerRow based on passed
  1091.  BMHD's (BitMapHeader) x, y, and numPlanes, then stores addresses into bitmap's
  1092.  planes[] of each plane within the passed raster. (All the planes in the raster
  1093.  form 1 contiguous CHIP mem block). Raster must be large enough for numPlanes
  1094.  * BMHD's (x+15)/8 * BMHD's y.
  1095.  After an ANIM is loaded, and you allocate a raster (via AllocRaster maybe)
  1096.  based on the BMHD's x, y, and numPlanes, you can use this routine to setup
  1097.  a BitMap structure in order to decompress the BODY with DecompBODY and
  1098.  start calling DecompDLTA.
  1099.  
  1100.  
  1101.             ================== DecompBODY() ===================
  1102.  DecompBODY(BODYdata, BMHD, Bitmap)
  1103.                     a0            a1        a2
  1104.  Decompresses a BODY chunk's data into the BitMap's planes based on the
  1105.  passed BMHD (BitMapHeader) chunk's w,y,numPlanes. This can be used to make
  1106.  the first frame of an ANIM.
  1107.  
  1108.  
  1109.             =================== DecompDLTA() ==================
  1110.  DecompDLTA(DLTAdata,Bitmap)
  1111.                     a0            a2
  1112.  Decompresses a DLTA chunk's data into the BitMap's planes (which must still
  1113.  have the previous frame's image). This routine calls MakeYTable and
  1114.  DecodeVKPlane, and can be used to "make" the next frame of an animation
  1115.  being double-buffered.
  1116.  
  1117.  
  1118.     You probably won't need to use these next 2 functions unless you're doing
  1119. something unusual with DLTA data.
  1120.  
  1121.         ===================== MakeYTable() =====================
  1122.  MakeYTable(width, height, table)
  1123.                     d0        d1         a0
  1124.  Makes a ytable for use with DecodeVKPlane. Table should be an memblock
  1125.  capable of holding 500 WORDs (1000 bytes).
  1126.  
  1127.  
  1128.     =================== DecodeVKPlane() ========================
  1129.        DecodeVKPlane(linebytes,ytable,in,out)
  1130.                         d0       d1   a0  a1
  1131.  
  1132.  By Jim Kent. Modified JG. Copyright 1987 Dancing Flame all rights reserved.
  1133.  Decompresses a DLTA chunk in vertical-byte-run-with-skips compression mode.
  1134.  
  1135.  where in is a bit-plane's worth of vertical-byte-run-with-skips data
  1136.  and out is a bit-plane that STILL has the image from last frame on it.
  1137.  Linebytes is the number of bytes-per-line (USHORT) in the out bitplane, and
  1138.  it should certainly be noted that the passed variable ytable must be
  1139.  initialized to point to a multiplication table of 0*linebytes, 1*linebytes
  1140.  ... n*linebytes  before this routine is called.
  1141.  Each entry in ytable is a USHORT.
  1142.  
  1143.  The format of "in":
  1144.    Each column of the bitplane is compressed separately.  A 320x200
  1145.    bitplane would have 40 columns of 200 bytes each.  The linebytes
  1146.    parameter is used to count through the columns, it is not in the
  1147.    "in" data, which is simply a concatenation of columns.
  1148.  
  1149.    Each columns is an op-count followed by a number of ops.
  1150.    If the op-count is zero, that's ok, it just means there's no change
  1151.    in this column from the last frame.
  1152.    The ops are of three classes, and followed by a varying amount of
  1153.    data depending on which class.
  1154.        1. Skip ops - this is a byte with the hi bit clear that says how many
  1155.           rows to move the "dest" pointer forward, ie to skip. It is non-
  1156.           zero
  1157.        2. Uniq ops - this is a byte with the hi bit set.  The hi bit is
  1158.           masked down and the remainder is a count of the number of bytes
  1159.           of data to copy literally.  It's of course followed by the
  1160.           data to copy.
  1161.        3. Same ops - this is a 0 byte followed by a count byte, followed
  1162.           by a byte value to repeat count times.
  1163.  
  1164.  
  1165. ***************************************************************************
  1166. 11). Image Size/Scaling
  1167.  
  1168.     When you ask the lib to load an image into an already opened window, some-
  1169. times the image won't be the exact same size as the window.  You may be trying
  1170. to load a HIRES picture (640 x 200) into a LORES screen (320 x 200), or vice
  1171. versa.  You may even be trying to load a picture larger than a standard Amiga
  1172. screen size (i.e. 660 x 220) into a standard size screen.  The library has a
  1173. routine that can automatically "scale" the picture to fit your window.  LORES
  1174. pictures will be "expanded" to fill a HIRES display, and HIRES will be "shrunk"
  1175. to fit a LORES.  Non-standard size pictures will likewise be scaled to fit a
  1176. standard screen.  Because of this, you never need worry about what size picture
  1177. a user has chosen to display in your window.  It will always fit.
  1178.     The lib's scaling routine makes multiple calls to graphics lib routines
  1179. which appear to do temporary mem alloc/dealloc.  Unfortunately, the dealloca-
  1180. tion is not done in the same order as the allocation. The net result: severe
  1181. memory fragmentation.  Oddly enough, this problem appears to only inflict LORES
  1182. pictures being scaled into a HIRES screen.  A HIRES picture into a LORES screen
  1183. works fine.  It appears likely that you may not be able to load a LORES picture
  1184. into a HIRES screen twice in a row.  Alas, the Amiga operating system is not
  1185. smart enough to re-coalese free blocks when it could.  You have to reboot the
  1186. computer.  You want smart memory coalesing, buy a MAC.  Despite this, I decided
  1187. that scaling the picture yielded more pleasing results than cropping it.  If
  1188. memory fragmentation problems persist, or you don't want a "smaller" picture to
  1189. be scaled to fill a "larger" display area, set the NOSCALE flag of the ILBM-
  1190. Frame's iUserFlags.  This will cause LORES pictures to only fill as much of a
  1191. larger display as they normally would.  Larger pictures are always scaled to
  1192. fit smaller displays.
  1193.     This scaling is meant to fit an image with a given width and height into
  1194. a screen with a different width and/or height.  The lib does not currently
  1195. adjust for different depths (number of planes).  For this reason, an image
  1196. with a depth of 5 will probably end up with "weird" colors when loaded into
  1197. a screen with a depth of 4.  Perhaps in the future, a routine will be added
  1198. to interpolate color tables if there appears to be interest in such a feature.
  1199.     Note that the process of scaling a picture to fit a different size screen
  1200. is notably slow.  Get used to it.
  1201.  
  1202.     Here is the routine that scales a section of one BitMap to fit into a
  1203. section of another RastPort.  It is passed a standard, graphics structure
  1204. called a Rectangle.
  1205.  
  1206. BOOL ScaleImage(dest_rectangle,source_rectangle,dest_rport,source_bitmap)
  1207.  d0                        a0                        a1                a2                a4
  1208.  
  1209. Passed pointers to the source bitmap, source rectangle structure (current
  1210. dimensions), destination rastport (to fit source into), and dest rectangle
  1211. struct (desired dimensions).  Scales the rectangular chunk (as described by
  1212. source_rectangle) of source bitmap into the rectangular chunk (as described by
  1213. dest_rectangle) of destination rastport.  It achieves this by remapping the
  1214. color value of each pixel, discarding or adding extra pixels per the scaling
  1215. dimensions using an array for the colors.  This does not create a larger or
  1216. smaller palette of colors if the # of planes is different. It simply fits one
  1217. image of given width and height into a different size raster. Alters the passed
  1218. sorce BitMap's planes while transfering. It only does one line at a time of the
  1219. source so it is SLOW.
  1220.  
  1221. Returns a 1 if success, 0 if error (no mem for color array).
  1222.  
  1223.  
  1224. ***************************************************************************
  1225. 12). ILBMlib Error Msgs
  1226.  
  1227.     Your calling routines should check all returned IFFP error codes.  Don't
  1228. press on after an error!  The lib routines try to have no side effects if an
  1229. error, except that partial I/O is sometimes unavoidable.  Any routine could
  1230. return DOS_ERROR.  In that case, ask DOS for the specific error code, if
  1231. desired, via the DOS library's IOErr() routine.
  1232.  
  1233.  Here is a routine to help display error messages to the user. This routine
  1234.  returns a pointer to a NULL-terminated string which describes the IFFP error
  1235.  number. You can then display this string to the user. See the INCLUDE files
  1236.  for the meaning of certain IFFP codes.
  1237.  
  1238.  String = GetIFFPMsg(IFFP)
  1239.     d0                        d0
  1240.  
  1241.  
  1242. ****************************************************************************
  1243. 13). Misc Routines
  1244.  
  1245. Here's a routine to make the mouse pointer in the passed window "disappear".
  1246. Use Intuition's ClearPointer() or SetPointer() to change it again later.
  1247.  
  1248.     BlankPointer(window)
  1249.                         a0
  1250.  
  1251.  
  1252. ****************************************************************************
  1253. 14). Additional Comments
  1254.  
  1255.     An effort was made to test all of the lib functions in a variety of ways,
  1256. but I'm sure that there are things which have escaped me.  Please send any
  1257. bug reports to the above address.  I am not offering free consultation to
  1258. anyone, but if you have a particular problem or question, I will try to
  1259. assist.  If a particular piece of code appears not to work with the library,
  1260. I would be interested in seeing the code.  There will almost certainly be new,
  1261. improved lib versions in the future.  These will be sent to Fred Fish.
  1262.     Obviously, more programming examples are needed.  In particular, a non-ILBM
  1263. FORM should be illustrated, maybe an 8SVX sample player.  Also, I would like
  1264. to do a simple ANIM player.  Unfortunately, I don't have time to do everything,
  1265. and since what you now have is free, it would be presumptious of you to expect
  1266. more.  If you write any code utilizing this library which you would like to
  1267. share with others, send it directly to Fred Fish.
  1268.     The modules that you should have are as follows:
  1269.  
  1270.     ILBMLib.Doc            this text file
  1271.     ilbm.library        the library (to be copied to your boot disk's libs drawer)
  1272.     IFF.i                    assembly language include file
  1273.     ILBM_Lib.h            C Include file
  1274.     ShowPic.c            a C example of using the lib's high level functions
  1275.     ILBMInterface.asm the awful "poot" that is required for any C example
  1276.     BasicILBM            an AmigaBasic example
  1277.     ilbm.bmap            the bmap file for the Basic example
  1278.     ilbm_lib.fd            the Basic fd file
  1279.     BasicUsers            additional info for Basic programmers
  1280.     ShowPic.asm            an assembly example using the high level functions
  1281.     ANIMInfo.asm        an assembly example of installing a custom FORM handler
  1282.                             and using mid/low level functions
  1283.     ANIMInfo                an executable of the above
  1284.     ShowPic                a re-entrant ILBM viewer with color-cycling (operates
  1285.                             like CBM's "Display" program)
  1286.     IFFinfo.c            a C example of installing a custom FORM handler and using
  1287.                             mid/low level functions
  1288.  
  1289.  
  1290. *****************************************************************************
  1291. 15). Acknowledgements
  1292.  
  1293.     The ilbm.library is based upon much code placed in the public domain by
  1294. several individuals. The programmers who offer such favors are the ones
  1295. who are really responsible for the Amiga still being a viable product.
  1296.     Once again, I want to mention the people who helped make this public
  1297. domain offering possible:
  1298.  
  1299.     Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
  1300.     C. Scheppner, CATS.
  1301.     Jim Kent, Dancing Flame.
  1302.  
  1303.     This is one of those few efforts in which I didn't steal "something" from
  1304. Bryce Nesbitt.  Bryce is such a good programmer and documentator that it is
  1305. a fluke that he actually works for CBM.
  1306.  
  1307.  
  1308. *****************************************************************************
  1309. 16). Scary Legal Stuff
  1310.  
  1311.     "OK. IF I USE IT, WHAT'S IT GONNA COST ME."
  1312.  
  1313.     The ilbm.library may be used by and FREELY distributed with any application
  1314. be it commercial or public domain.
  1315.     There are no pagan users fees, Trump-esque licenses, or other forms of rabid
  1316. capitalist trickery associated with using this library and its support files.
  1317. You do not even have to acknowledge the secret of your expedient and memory
  1318. efficient IFF I/O routines.
  1319.     The only limitation is that you may not alter the actual executable of the
  1320. ilbm.library, nor sell the library and its support files as a distinct product
  1321. (i.e. represent it as such).
  1322.  
  1323.  
  1324. ============================= FOOTNOTES ===============================
  1325.  
  1326.  ¹    The routines that read and write data to disc (IFFReadBytes and IFFWrite-
  1327.     bytes) use unbuffered I/O. An application that uses the buffered version
  1328.     of EA's code will probably be faster but certainly not as memory effic-
  1329.     ient. Wouldn't you rather wait an extra 15 seconds to load an ANIM file
  1330.     rather than not be able to view it at all because the loader is sucking
  1331.     up RAM? Besides, if you want to waste memory with buffers that remain idle
  1332.     except during disk I/O, why not use the CLI ADDBUFFERS command. This way,
  1333.     you can speed up the lib to comparable levels with the buffered IFF code,
  1334.     and also allow other disc I/O code to benefit. I realize that the concept
  1335.     of conserving RAM is not popular amoung non-assembly programmers, most
  1336.     of whom write applications which unnecessarily require 1 MEG to run well.
  1337.  
  1338.  ²    In order to handle PROPs more efficiently and with less stack use than
  1339.     the original EA code, I made the following limitation.
  1340.         There should be no LIST imbedded within a LIST.
  1341.     In a file that violates this rule, PROP data from the outer LIST may
  1342.     affect chunks inside the inner LIST. Since I have never seen imbedded
  1343.     LISTs, I decided that such abominations don't deserve special treatment.
  1344.